
(function($ , undefined) {
	var multiplible = 'multiple' in document.createElement('INPUT');
	var hasFileList = 'FileList' in window;//file list enabled in modern browsers
	var hasFileReader = 'FileReader' in window;
	var hasFile = 'File' in window;
	var ace={vars:{icon:" ace-icon ",".icon":".ace-icon"},click_event:"click"};
	var Ace_File_Input = function(element , settings) {
		var self = this;
		this.settings = $.extend({}, $.fn.ace_file_input.defaults, settings);

		this.$element = $(element);
		this.element = element;
		this.disabled = false;
		this.can_reset = true;
		

		this.$element
		.off('change.ace_inner_call')
		.on('change.ace_inner_call', function(e , ace_inner_call){
			if(self.disabled) return;
		
			if(ace_inner_call === true) return;//this change event is called from above drop event and extra checkings are taken care of there
			return handle_on_change.call(self);
			//if(ret === false) e.preventDefault();
		});

		var parent_label = this.$element.closest('label').css({'display':'block'});
		var tagName = parent_label.length == 0 ? 'label' : 'span';//if not inside a "LABEL" tag, use "LABEL" tag, otherwise use "SPAN"
		this.$element.wrap('<'+tagName+' class="ace-file-input" />');

		this.apply_settings();
		this.reset_input_field();//for firefox as it keeps selected file after refresh
		if(this.settings.imgSrcUrl && this.settings.imgSrcUrl.length>0){
			initPreviewImage(this,this.settings.imgSrcUrl)
		}
	};
	Ace_File_Input.error = {
		'FILE_LOAD_FAILED' : 1,
		'IMAGE_LOAD_FAILED' : 2,
		'THUMBNAIL_FAILED' : 3
	};


	Ace_File_Input.prototype.apply_settings = function() {
		var self = this;
		if(!self.settings.showFileName) this.settings.no_file="";
		this.multi = this.$element.attr('multiple') && multiplible;
		this.well_style = this.settings.style == 'well';

		if(this.well_style) this.$element.parent().addClass('ace-file-multiple');
		 else this.$element.parent().removeClass('ace-file-multiple');


		this.$element.parent().find(':not(input[type=file])').remove();//remove all except our input, good for when changing settings
		var msgtop=typeof this.settings.tipmsg_top =="undefined" || this.settings.tipmsg_top.length==0 ?"":"<span class='file-msg-top'>"+this.settings.tipmsg_top +"</span>";
		var msgbottom=typeof this.settings.tipmsg_bottom =="undefined" || this.settings.tipmsg_bottom.length==0 ?"":"<span class='file-msg-botton'>"+this.settings.tipmsg_bottom +"</span>";
		var msgtitle=typeof this.settings.tipmsg_title =="undefined" || this.settings.tipmsg_title.length==0 ?"":"<span class='file-msg-title'>"+self.settings.tipmsg_title +"</span>";
		this.$element.after('<span class="ace-file-container" data-title="'+this.settings.tipmsg_top+'"><span class="ace-file-name" data-title="'+this.settings.no_file+'">'+msgtop+(this.settings.no_icon ? '<i class="'+ ace.vars['icon'] + this.settings.no_icon+'"></i>' : '')+(msgbottom+msgtitle)+'</span></span>');
		this.$label = this.$element.next();
		this.$container = this.$element.closest('.ace-file-input');

		var remove_btn = !!this.settings.icon_remove;
		if(remove_btn) {
			var btn = 
			$('<a class="remove" href="javascript:"><i class="'+ ace.vars['icon'] + this.settings.icon_remove+'"></i></a>')
			.appendTo(this.$element.parent());

			btn.on(ace.click_event, function(e){
				e.preventDefault();
				if( !self.can_reset ) return false;
				
				var ret = true;
				if(self.settings.before_remove) ret = self.settings.before_remove.call(self.element,btn);
				if(!ret) return false;

				var r = self.reset_input();
				return false;
			});
		}


		if(this.settings.droppable && hasFileList) {
			enable_drop_functionality.call(this);
		}
	};

	Ace_File_Input.prototype.show_file_list = function($files , inner_call) {
		var files = typeof $files === "undefined" ? this.$element.data('ace_input_files') : $files;
		if(!files || files.length == 0) return;
		
		//////////////////////////////////////////////////////////////////
		
		if(this.well_style) {
			this.$label.find('.ace-file-name').remove();
			if(!this.settings.btn_change) this.$label.addClass('hide-placeholder');
		}
		this.$label.attr('data-title', this.settings.btn_change).addClass('selected');
		
		for (var i = 0; i < files.length; i++) {
			var filename = '', format = false;
			if(typeof files[i] === "string") filename = files[i];
			else if(hasFile && files[i] instanceof File) filename = $.trim( files[i].name );
			else if(files[i] instanceof Object && files[i].hasOwnProperty('name')) {
				//format & name specified by user (pre-displaying name, etc)
				filename = files[i].name;
				if(files[i].hasOwnProperty('type')) format = files[i].type;
				if(!files[i].hasOwnProperty('path')) files[i].path = files[i].name;
			}
			else continue;
			
			var index = filename.lastIndexOf("\\") + 1;
			if(index == 0)index = filename.lastIndexOf("/") + 1;
			filename = filename.substr(index);
			
			if(format == false) {
				if((/\.(jpe?g|png|gif|svg|bmp|tiff?)$/i).test(filename)) {				
					format = 'image';
				}
				else if((/\.(mpe?g|flv|mov|avi|swf|mp4|mkv|webm|wmv|3gp)$/i).test(filename)) {
					format = 'video';
				}
				else if((/\.(mp3|ogg|wav|wma|amr|aac)$/i).test(filename)) {
					format = 'audio';
				}
				else format = 'file';
			}
			
			var fileIcons = {
				'file' : 'fa fa-file',
				'image' : 'fa fa-picture-o file-image',
				'video' : 'fa fa-film file-video',
				'audio' : 'fa fa-music file-audio'
			};
			var fileIcon = fileIcons[format];
			var self = this;
			var dataFileName=self.settings.isImgType?(this.settings.showFileName?filename:""):filename;
			if(!this.well_style) this.$label.find('.ace-file-name').attr({'data-title':dataFileName}).find(ace.vars['.icon']).attr('class', ace.vars['icon'] + fileIcon);
			else {
				var msgtop=typeof self.settings.tipmsg_top =="undefined" || self.settings.tipmsg_top.length==0 ?"":"<span class='file-msg-top'>"+self.settings.tipmsg_top +"</span>";
				var msgbottom=typeof self.settings.tipmsg_bottom =="undefined" || self.settings.tipmsg_bottom.length==0 ?"":"<span class='file-msg-botton'>"+self.settings.tipmsg_bottom +"</span>";
				var msgtitle=typeof self.settings.tipmsg_title =="undefined" || self.settings.tipmsg_title.length==0 ?"":"<span class='file-msg-title'>"+self.settings.tipmsg_title +"</span>";
				self.$label.append('<span class="ace-file-name" data-title="'+dataFileName+'">'+msgtop+'<i class="'+ ace.vars['icon'] + fileIcon+'"></i>'+(msgbottom+msgtitle)+'</span>');
				if(self.settings.isImgType){
					var type = (inner_call === true && hasFile && files[i] instanceof File) ? $.trim(files[i].type) : '';
					var can_preview = hasFileReader && self.settings.thumbnail && ( (type.length > 0 && type.match('image')) || (type.length == 0 && format == 'image') );//the second one is for older Android's default browser which gives an empty text for file.type
					if(can_preview) {
						$.when(preview_image.call(self, files[i])).fail(function(result){
							//called on failure to load preview
							if(self.settings.preview_error) self.settings.preview_error.call(self, filename, result.code);
						})
					}
				}
			}
		}
		return true;
	};
	
	Ace_File_Input.prototype.reset_input = function() {
	    this.reset_input_ui();
		this.reset_input_field();
	};
	
	Ace_File_Input.prototype.reset_input_ui = function() {
		if(!this.settings.showFileName) this.settings.no_file="";
		var $lab=this.$label;
		$lab.attr({'data-title':this.settings.tipmsg_top, 'class':'ace-file-container'})
			.find('.ace-file-name:first').attr({'data-title':this.settings.no_file , 'class':'ace-file-name'})
			.find(ace.vars['.icon']).attr('class', ace.vars['icon'] + this.settings.no_icon)
			.siblings('img').remove();
		$lab.find(".file-msg-top").removeClass("hide-placeholder");
		$lab.find(".file-msg-botton").removeClass("hide-placeholder");
		if(!this.settings.no_icon) $lab.find(ace.vars['.icon']).remove();
		$lab.find('.ace-file-name').not(':first').remove();
		
		this.reset_input_data();
		
		//if(ace.vars['old_ie']) ace.helper.redraw(this.$container[0]);
	};
	Ace_File_Input.prototype.reset_input_field = function() {
		//http://stackoverflow.com/questions/1043957/clearing-input-type-file-using-jquery/13351234#13351234
		this.$element.wrap('<form>').parent().get(0).reset();
		this.$element.unwrap();
		
		//strangely when reset is called on this temporary inner form
		//only **IE9/10** trigger 'reset' on the outer form as well
		//and as we have mentioned to reset input on outer form reset
		//it causes infinite recusrsion by coming back to reset_input_field
		//thus calling reset again and again and again
		//so because when "reset" button of outer form is hit, file input is automatically reset
		//we just reset_input_ui to avoid recursion
	};
	Ace_File_Input.prototype.reset_input_data = function() {
		if(this.$element.data('ace_input_files')) {
			this.$element.removeData('ace_input_files');
			this.$element.removeData('ace_input_method');
		}
	};

	Ace_File_Input.prototype.enable_reset = function(can_reset) {
		this.can_reset = can_reset;
	};

	Ace_File_Input.prototype.disable = function() {
		this.disabled = true;
		this.$element.attr('disabled', 'disabled').addClass('disabled');
	};
	Ace_File_Input.prototype.enable = function() {
		this.disabled = false;
		this.$element.removeAttr('disabled').removeClass('disabled');
	};

	Ace_File_Input.prototype.files = function() {
		return $(this).data('ace_input_files') || null;
	};
	Ace_File_Input.prototype.method = function() {
		return $(this).data('ace_input_method') || '';
	};
	
	Ace_File_Input.prototype.update_settings = function(new_settings) {
		this.settings = $.extend({}, this.settings, new_settings);
		this.apply_settings();
	};
	
	Ace_File_Input.prototype.loading = function(is_loading) {
		if(is_loading === false) {
			this.$container.find('.ace-file-overlay').remove();
			this.element.removeAttribute('readonly');
		}
		else {
			var inside = typeof is_loading === 'string' ? is_loading : '<i class="overlay-content fa fa-spin fa-spinner orange2 fa-2x"></i>';
			var loader = this.$container.find('.ace-file-overlay');
			if(loader.length == 0) {
				loader = $('<div class="ace-file-overlay"></div>').appendTo(this.$container);
				loader.on('click tap', function(e) {
					e.stopImmediatePropagation();
					e.preventDefault();
					return false;
				});
				
				this.element.setAttribute('readonly' , 'true');//for IE
			}
			loader.empty().append(inside);
		}
	};



	var enable_drop_functionality = function() {
		var self = this;
		
		var dropbox = this.$element.parent();
		dropbox
		.off('dragenter')
		.on('dragenter', function(e){
			e.preventDefault();
			e.stopPropagation();
		})
		.off('dragover')
		.on('dragover', function(e){
			e.preventDefault();
			e.stopPropagation();
		})
		.off('drop')
		.on('drop', function(e){
			e.preventDefault();
			e.stopPropagation();

			if(self.disabled) return;
		
			var dt = e.originalEvent.dataTransfer;
			var file_list = dt.files;
			if(!self.multi && file_list.length > 1) {//single file upload, but dragged multiple files
				var tmpfiles = [];
				tmpfiles.push(file_list[0]);
				file_list = tmpfiles;//keep only first file
			}
			
			
			file_list = processFiles.call(self, file_list, true);//true means files have been selected, not dropped
			if(file_list === false) return false;

			self.$element.data('ace_input_method', 'drop');
			self.$element.data('ace_input_files', file_list);//save files data to be used later by user

			//self.show_file_list(file_list , true);
			ajaxUpLoad.call(self,file_list);
			self.$element.triggerHandler('change' , [true]);//true means ace_inner_call
			return true;
		});
	};
	var handle_on_change = function() {
		var file_list = this.element.files || [this.element.value];/** make it an array */
		
		file_list = processFiles.call(this, file_list, false);//false means files have been selected, not dropped
		if(file_list === false) return false;
		
		this.$element.data('ace_input_method', 'select');
		this.$element.data('ace_input_files', file_list);
		ajaxUpLoad.call(this,file_list);
	};
	var ajaxUpLoad=function(file_list){
		var self=this;
		if(self.settings.ajaxCallback){
		    if (self.settings.isUseBase64) {
				$.when(checkFileWh.call(self, file_list[0])).done(function(result){
					if(result.success){
						self.settings.ajaxCallback(self,result.imgbase64,function(){self.show_file_list(file_list , true);});
					}else{
						if(self.settings.checkCallback) self.settings.checkCallback(self.element, result.errmsg);
					}
				}).fail(function(result){self.show_file_list(file_list , true);})
			}else{
				self.settings.ajaxCallback(self,file_list[0],function(){self.show_file_list(file_list , true);});
			}
		}else{
			if(self.settings.successCallback){self.settings.successCallback(file_list)}
			self.show_file_list(file_list , true);
		}
		//self.show_file_list(file_list , true);
	};
	var checkFileWh = function(file) {
		//var self = this;
		var whType    = this.settings.whType || false;
		var sw=this.settings.sizeWidth;
		var sh=this.settings.sizeHeight;
		var deferred = new $.Deferred;
		if(hasFile && file instanceof File) {
			var reader = new FileReader();
			reader.onload = function (e) {
				var imgCheck = $("<img />").attr("src", e.target.result).get(0);
				setTimeout(function(){
					var w=imgCheck.width;
					var h=imgCheck.height;
					var bType=true;
					if(whType && sw && sh) {
						switch (whType) {
							case "greater":
								bType = w >= sw && h >= sh;
								break;
							case "less":
								bType = w <= sw && h <= sh;
								break;
							case "equal":
								bType = w == sw && h == sh;
								break;
						}
					}
					var result=bType?{success:true,imgbase64: e.target.result}:{success:false,errmsg:["图片尺寸不正确"]};
					deferred.resolve(result);
				},200);
			};
			reader.readAsDataURL(file);
		}

		return deferred.promise();
	};

	var initPreviewImage = function(self,srcurl) {
		//var self = this;
		self.$label.addClass('hide-placeholder selected').removeAttr("data-title");
		self.$label.find(".file-msg-top").addClass("hide-placeholder");
		self.$label.find(".file-msg-botton").addClass("hide-placeholder");
		var $span = self.$label.find('.ace-file-name:last');//it should be out of onload, otherwise all onloads may target the same span because of delays

		var deferred = new $.Deferred;
		var getImage = function(src) {
			$span.prepend("<img class='middle' style='display:none;' />");
			var img = $span.find('img:last').get(0);
			$span.addClass("img-upload-success");
			$(img).one('load', function() {
				imgLoaded.call(null, img);
			}).one('error', function() {
				imgFailed.call(null, img);
			});
			img.src = src;
		};
		var imgLoaded = function(img) {
			//if image loaded successfully
			var size = 50;
			if(self.settings.thumbnail == 'large') size = 150;
			else if(self.settings.thumbnail == 'fit') size = $span.width()==0?self.settings.spanWidth:$span.width();
			$span.addClass(size > 50 ? 'large' : '');

			var thumb = get_thumbnail_Preview(img, size/**, file.type*/);
			if(thumb == null) {
				//if making thumbnail fails
				$(this).remove();
				deferred.reject({code:Ace_File_Input.error['THUMBNAIL_FAILED']});
				return;
			}
			var w = thumb.w, h = thumb.h;
			if(self.settings.thumbnail == 'small') {w=h=size;}
			$(img).data({'thumb':thumb.src,'original':img.currentSrc});
			if(thumb.src.length==0){
				//thumb.src=img.src;
				//
				if(w>0 && h>0){
					$(img).css({'background-image':'url('+img.src+')' , width:w, height:h,'background-size': ''+w+'px '+h+'px','background-color':'rgba(0, 0, 0, 0)'}).attr({src:img.src}).show();
				}else{
					if(img.src.length>0){
						$span.addClass(img.width > 50 ? 'large' : '');
						$(img).css({'background-image':'url('+img.src+')', width:img.width, height:img.height,'background-size': ''+img.width+'px '+img.height+'px','background-color':'rgba(0, 0, 0, 0)'}).attr({src:img.src}).show();
					}else{
						$(img).remove();
					}

				}
			}else{
				$(img).css({'background-image':'url('+thumb.src+')' , width:w, height:h,'background-size': ''+w+'px '+h+'px','background-color':'rgba(0, 0, 0, 0)'}).attr({src:''}).show();
			}
			///////////////////
			deferred.resolve();
		};
		var imgFailed = function(img) {
			//for example when a file has image extenstion, but format is something else
			$span.find('img').remove();
			deferred.reject({code:Ace_File_Input.error['IMAGE_LOAD_FAILED']});
		};
		getImage(srcurl);
		return deferred.promise();
	};

	var get_thumbnail_Preview = function(img, size, type) {
		var w = img.width, h = img.height;

		//**IE10** is not giving correct width using img.width so we use $(img).width()
		w = w > 0 ? w : $(img).width();
		h = h > 0 ? h : $(img).height();

		if(w > size || h > size) {
			if(w > h) {
				h = parseInt(size/w * h);
				w = size;
			} else {
				w = parseInt(size/h * w);
				h = size;
			}
		}


		var dataURL;
		try {
			var canvas = document.createElement('canvas');
			canvas.width = w; canvas.height = h;
			var context = canvas.getContext('2d');
			context.drawImage(img, 0, 0, img.width, img.height, 0, 0, w, h);
			dataURL = canvas.toDataURL(/*type == 'image/jpeg' ? type : 'image/png', 10*/)
		} catch(e) {
			dataURL = null;
		}
		if(dataURL==null || dataURL=="data:,"){
			return {src: "", w:w, h:h};
		}else{
			return {src: dataURL, w:w, h:h};
		}
	};

	var preview_image = function(file) {
		var self = this;
		var $span = self.$label.find('.ace-file-name:last');//it should be out of onload, otherwise all onloads may target the same span because of delays
		
		var deferred = new $.Deferred;
		var getImage = function(src) {
			$span.prepend("<img class='middle' style='display:none;' />");
			var img = $span.find('img:last').get(0);
			$span.addClass("img-upload-success");
			$(img).one('load', function(e) {
				imgLoaded.call(null, img);
			}).one('error', function() {
				imgFailed.call(null, img);
			});

			img.src = src;
		};
		var imgLoaded = function(img) {
			//if image loaded successfully
			var size = 50;
			if(self.settings.thumbnail == 'large') size = 150;
			else if(self.settings.thumbnail == 'fit') size = $span.width();
			$span.addClass(size > 50 ? 'large' : '');
			var imgsrc=img.src;
			var isGif=img.src.indexOf("data:image/gif")>-1;
			var thumb = get_thumbnail(img, size/**, file.type*/);
			if(thumb == null) {
				//if making thumbnail fails
				$(this).remove();
				deferred.reject({code:Ace_File_Input.error['THUMBNAIL_FAILED']});
				return;
			}
			var w = thumb.w, h = thumb.h;
			var showSrc=isGif?img.src:thumb.src;
			if(self.settings.thumbnail == 'small') {w=h=size;}
			$(img).css({'background-image':'url('+showSrc+')' , width:w, height:h,'background-size': ''+w+'px '+h+'px','background-color':'rgba(0, 0, 0, 0)'})
					.data('thumb', showSrc)
					.data('original', img.currentSrc)
					.attr({src:''})
					.show();

			///////////////////
			deferred.resolve();
		};
		var imgFailed = function(img) {
			//for example when a file has image extenstion, but format is something else
			$span.find('img').remove();
			deferred.reject({code:Ace_File_Input.error['IMAGE_LOAD_FAILED']});
		};
		
		if(hasFile && file instanceof File) {
			var reader = new FileReader();
			reader.onload = function (e) {
				getImage(e.target.result);
			};
			reader.onerror = function (e) {
				deferred.reject({code:Ace_File_Input.error['FILE_LOAD_FAILED']});
			};
			reader.readAsDataURL(file);
		}
		else {
			if(file instanceof Object && file.hasOwnProperty('path')) {
				getImage(file.path);//file is a file name (path) --- this is used to pre-show user-selected image
			}
		}
		return deferred.promise();
	};

	var get_thumbnail = function(img, size, type) {
		var w = img.width, h = img.height;
		
		//**IE10** is not giving correct width using img.width so we use $(img).width()
		w = w > 0 ? w : $(img).width();
		h = h > 0 ? h : $(img).height();

		if(w > size || h > size) {
		  if(w > h) {
			h = parseInt(size/w * h);
			w = size;
		  } else {
			w = parseInt(size/h * w);
			h = size;
		  }
		}


		var dataURL;
		try {
			var canvas = document.createElement('canvas');
			canvas.width = w; canvas.height = h;
			var context = canvas.getContext('2d');
			context.drawImage(img, 0, 0, img.width, img.height, 0, 0, w, h);
			dataURL = canvas.toDataURL("image/gif")
		} catch(e) {
			dataURL = null;
		}
		if(! dataURL) return null;
		

		//there was only one image that failed in firefox completely randomly! so let's double check things
		if( !( /^data\:image\/(png|jpe?g|gif);base64,[0-9A-Za-z\+\/\=]+$/.test(dataURL)) ) dataURL = null;
		if(! dataURL) return null;
		

		return {src: dataURL, w:w, h:h};
	};
	

	
	var processFiles = function(file_list, dropped) {
		var ret = checkFileList.call(this, file_list, dropped);
		if(ret === -1) {
			this.reset_input();
			return false;
		}
		if( !ret || ret.length === 0 ) {
			if( !this.$element.data('ace_input_files') ) this.reset_input();
			//if nothing selected before, reset because of the newly unacceptable (ret=false||length=0) selection
			//otherwise leave the previous selection intact?!!!
			return false;
		}
		if (ret instanceof Array || (hasFileList && ret instanceof FileList)) file_list = ret;
		
		
		ret = true;
		if(this.settings.before_change) ret = this.settings.before_change(this.element, file_list, dropped);
		if(ret === -1) {
			this.reset_input();
			return false;
		}
		if(!ret || ret.length === 0) {
			if( !this.$element.data('ace_input_files') ) this.reset_input();
			return false;
		}
		
		//inside before_change you can return a modified File Array as result
		if (ret instanceof Array || (hasFileList && ret instanceof FileList)) file_list = ret;
		
		return file_list;
	};
	
	
	var getExtRegex = function(ext) {
		if(!ext) return null;
		if(typeof ext === 'string') ext = [ext];
		if(ext.length === 0) return null;
		return new RegExp("\.(?:"+ext.join('|')+")$", "i");
	};
	var getMimeRegex = function(mime) {
		if(!mime) return null;
		if(typeof mime === 'string') mime = [mime];
		if(mime.length === 0) return null;
		return new RegExp("^(?:"+mime.join('|').replace(/\//g, "\\/")+")$", "i");
	};

	var checkFileList = function(files, dropped) {
		var allowExt   = getExtRegex(this.settings.allowExt);

		var denyExt    = getExtRegex(this.settings.denyExt);

		var allowMime  = getMimeRegex(this.settings.allowMime);

		var denyMime   = getMimeRegex(this.settings.denyMime);

		var maxSize    =this.settings.maxSize?this.settings.maxSize*1024 : false;

		if( !(allowExt || denyExt || allowMime || denyMime || maxSize) ) return true;//no checking required


		var safe_files = [];
		var error_list = {};
		for(var f = 0; f < files.length; f++) {
			var file = files[f];

			//file is either a string(file name) or a File object
			var filename = !hasFile ? file : file.name;
			if( allowExt && !allowExt.test(filename) ) {
				//extension not matching whitelist, so drop it
				if(!('ext' in error_list)) error_list['ext'] = [];
				error_list['ext'].push(filename);

				continue;
			} else if( denyExt && denyExt.test(filename) ) {
				//extension is matching blacklist, so drop it
				if(!('ext' in error_list)) error_list['ext'] = [];
				error_list['ext'].push(filename);

				continue;
			}

			var type;
			if( !hasFile ) {
				//in browsers that don't support FileReader API
				safe_files.push(file);
				continue;
			}
			else if((type = $.trim(file.type)).length > 0) {
				//there is a mimetype for file so let's check against are rules
				if( allowMime && !allowMime.test(type) ) {
					//mimeType is not matching whitelist, so drop it
					if(!('mime' in error_list)) error_list['mime'] = [];
					error_list['mime'].push(filename);
					continue;
				}
				else if( denyMime && denyMime.test(type) ) {
					//mimeType is matching blacklist, so drop it
					if(!('mime' in error_list)) error_list['mime'] = [];
					error_list['mime'].push(filename);
					continue;
				}
			}

			if( maxSize && file.size > maxSize ) {
				//file size is not acceptable
				if(!('size' in error_list)) error_list['size'] = [];
				error_list['size'].push(filename);
				continue;
			}

			safe_files.push(file)
		}



		if(safe_files.length === files.length) return files;//return original file list if all are valid

		/////////
		var error_count = {'ext': 0, 'mime': 0, 'size': 0};
		if( 'ext' in error_list ) error_count['ext'] = error_list['ext'].length;
		if( 'mime' in error_list ) error_count['mime'] = error_list['mime'].length;
		if( 'size' in error_list ) error_count['size'] = error_list['size'].length;

		var event;
		var eventinfo={'file_count': files.length, 'invalid_count' : files.length - safe_files.length, 'error_list' : error_list, 'error_count' : error_count, 'dropped': dropped};

		this.$element.trigger(event = new $.Event('file.error.ace'),eventinfo);
		var arrErrMsg=[];
		if(error_count.ext>0 || error_count.mime>0 )arrErrMsg.push("上传文件类型不正确");
		if(error_count.size>0)arrErrMsg.push("上传文件大小不正确");
		if(this.settings.checkCallback) this.settings.checkCallback(this.element, arrErrMsg);
		if ( event.isDefaultPrevented() ) return -1;//it will reset input
		//////////

		return safe_files;//return safe_files
	};

	///////////////////////////////////////////
	$.fn.aceFileInput = $.fn.ace_file_input = function (option,value) {
		var retval;

		var $set = this.each(function () {
			var $this = $(this);
			var data = $this.data('ace_file_input');
			var options = typeof option === 'object' && option;

			if (!data) $this.data('ace_file_input', (data = new Ace_File_Input(this, options)));
			if (typeof option === 'string') retval = data[option](value);
		});

		return (retval === undefined) ? $set : retval;
	};


	$.fn.aceFileInput.defaults = $.fn.ace_file_input.defaults = {
		style: false,
		no_file: 'No File ...',
		no_icon: 'fa fa-upload',
		tipmsg_top: 'Choose',
		tipmsg_bottom:"",
		btn_change: 'Change',
		icon_remove: 'fa fa-times',
		droppable: false,
		thumbnail: false,//large, fit, small
		
		allowExt: null,
		denyExt: null,
		allowMime: null,
		denyMime: null,
		maxSize: false,
		
		//callbacks
		before_change: null,
		before_remove: null,
		preview_error: null,

		////ext
		whType:false,// false || greater ,less,equal
		sizeWidth:0,
		sizeHeight:0,
		checkCallback:null,
		ajaxCallback:null,
		imgSrcUrl:false,
		showFileName:true,
		isImgType:true,
		spanWidth:0,
		tipmsg_title:""
     }


})(window.jQuery);
